const {
  log,
  done,
  info
} = require("@vue/cli-shared-utils")

const path = require("path")
const fs = require("fs-extra")
const { spawn } = require('child_process')
const readline = require("readline")

const { webpackMain } = require('../../utils')

// ElectronServer
module.exports = class {
  child = null
  queuedBuilds = 0
  firstBundleCompleted = false
  constructor(api, opts) {
    this.api = api
    this.opts = opts
    this.outputDir = opts.outputDir

    // 需要监听的文件
    let watchPaths = ((list) => {
      list = typeof list === 'string' ? [list] : list
      let basePath = api.resolve(opts.srcDir), file_list = []
      // each
      list.forEach(item => {
        file_list.push(
          path.join(basePath, item === "*" ? '' : item)
        );
      });
      return file_list
    })(opts.mainFileChangeWatchs);
    this.watchFileChange(watchPaths)

    this.start()

    this.watchSignal()
  }
  /**
   * 启动 打包和electron
   */
  async start() {
    info("Clean cache output...")
    fs.remove(this.opts.outputDir)
    info("Start Electron...")
    this.queuedBuilds++

    /* 编译打包 main-process 主进程文件 */
    await webpackMain(this.api, this.opts);
    
    this.launchElectron()
  }
  /**
   * 开始加载 Electron 进程
   */
  launchElectron() {
    this.firstBundleCompleted = true
    if (this.child) {
      this.child.removeListener("exit", this.onChildExit)
      this.child.removeListener("close", this.onChildExit)
    }
    this.stop()// 杀死现有进程
    // 如果捆绑了新的后台文件，则不启动
    this.queuedBuilds--
    if (this.queuedBuilds > 0) return

    info("Launching Electron...")
    // 运行 electron
    this.child = spawn(
      require(this.api.resolve('./node_modules/electron')),
      [this.outputDir],
      {
        cwd: this.api.resolve("."),
        env: {
          ...process.env,
          // 禁用 Electron 安全警告
          ELECTRON_DISABLE_SECURITY_WARNINGS: true
        }
      }
    )
    // Pipe output to console 将输出流混合推向控制台
    this.child.stdout.pipe(process.stdout)
    this.child.stderr.pipe(process.stderr)
    // 监听 electron 进程关闭事件
    this.child.on("exit", this.onChildExit);
    this.child.on("close", this.onChildExit);
  }
  /**
   * 暂停/关闭 Electron 进程
   */
  stop() {
    if (!this.child) {
      return
    }
    if(this.child.kill) {
      log(`Force killing Electron (process #${this.child.pid})`)
      process.kill(this.child.pid)
      this.child = null
    }
  }
  /**
   * 子进程关闭/退出 触发主进程退出
   * @param {*} code 
   */
  onChildExit(code) {
    process.exit(0)
  }
  /**
   * 监听文件变动
   * @param { Array } list 
   */
  watchFileChange(list) {
    // 主文件修改监听变动
    const chokidar = require("chokidar")
    chokidar.watch(list).on("all", (evt, path) => {
      if (this.firstBundleCompleted) {
        log(`[File ${evt}] - ${path}`)
        this.start()
      }
    });
  }
  /**
   * 监听进程信号
   */
  watchSignal() {
    // 收到 SIGINT、SIGTERM 信号时杀死进程
    const signalHandler = (e) => {
      if (!this.child) {
        process.exit(0)
      }
      this.stop()
    }
    if (!process.env.IS_TEST) process.on("SIGINT", signalHandler)
    if (!process.env.IS_TEST) process.on("SIGTERM", signalHandler)
    // Handle Ctrl+C on Windows
    if (process.platform === "win32" && !process.env.IS_TEST) {
      readline
        .createInterface({
          input: process.stdin,
          output: process.stdout
        })
        .on("SIGINT", () => {
          process.emit("SIGINT")
        })
    }
  }
}
